package com.ljh.lottery.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ljh.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.ljh.lottery.common.exception.ServiceException;
import com.ljh.lottery.common.utils.BeanTransUtil;
import com.ljh.lottery.common.utils.JwtUtil;
import com.ljh.lottery.common.utils.RegexUtil;
import com.ljh.lottery.pojo.dataobject.Encrypt;
import com.ljh.lottery.mapper.UserMapper;
import com.ljh.lottery.pojo.dataobject.UserDO;
import com.ljh.lottery.pojo.request.ShortMessageLoginRequest;
import com.ljh.lottery.pojo.request.UserLoginRequest;
import com.ljh.lottery.pojo.request.UserPasswordLoginRequest;
import com.ljh.lottery.pojo.request.UserRegisterRequest;
import com.ljh.lottery.service.UserService;
import com.ljh.lottery.service.VerificationCodeService;
import com.ljh.lottery.service.dto.UserDTO;
import com.ljh.lottery.service.dto.UserLoginDTO;
import com.ljh.lottery.service.dto.UserRegisterDTO;
import com.ljh.lottery.service.enums.UserIdentityEnum;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;

/**
 * @author: Themberfue
 * @date: 2025/4/8 13:54
 * @description:
 */
@Service
public class UserServiceImpl implements UserService {
    private static final Integer MAIL = 1;
    private static final Integer PHONE = 2;

    @Autowired
    UserMapper userMapper;

    @Resource(name = "verificationCodeServiceImpl")
    VerificationCodeService verificationCodeService;

    @Override
    public UserRegisterDTO register(UserRegisterRequest param) {
        // TODO 1. 校验注册信息
        checkRegisterInfo(param);

        // TODO 2. 手机号加密 => 通过 TypeHandler 自动加密

        // TODO 3. 构造对象、密码加密
        UserDO userDO = BeanTransUtil.transToUserDO(param);

        // TODO 4. 存入数据库
        userMapper.insert(userDO);

        // TODO 5. 构造返回值
        return BeanTransUtil.transToUserRegisterDTO(userDO);
    }

    @Override
    public UserLoginDTO login(UserLoginRequest param) {
        UserLoginDTO userLoginDTO;
        // ! 类型检查同时类型转换，java 14 及以上版本
        if (param instanceof UserPasswordLoginRequest loginRequest) {
            userLoginDTO = loginByUserPassword(loginRequest);
        } else if (param instanceof ShortMessageLoginRequest loginRequest) {
            userLoginDTO = loginByShortMessage(loginRequest);
        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXISTS);
        }
        return userLoginDTO;
    }

    @Override
    public List<UserDTO> getAllUserInfo(UserIdentityEnum userIdentity) {
        // TODO 1. 转换身份信息数据类型
        String identity = null == userIdentity ? null : userIdentity.name();

        // TODO 2. 获取用户数据
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        if (identity != null)
            queryWrapper.lambda()
                .eq(UserDO::getIdentity, identity);

        queryWrapper.lambda()
                .orderByDesc(UserDO::getId);

        return userMapper.selectList(queryWrapper)
                .stream()
                .map(BeanTransUtil::transToUserDTO)
                .toList();
    }

    /**
     * 根据密码登录
     * @param loginRequest 登录信息
     * @return UserLoginDTO
     */
    private UserLoginDTO loginByUserPassword(UserPasswordLoginRequest loginRequest) {
        // TODO 1. 获取用户数据
        QueryWrapper<UserDO> queryWrapper = null;
        UserDO userDO = null;

        // TODO 2. 查看是手机登录还是密码登录
        if (RegexUtil.checkMobile(loginRequest.getLoginName())) { // ? 为手机号登录
            queryWrapper = query(loginRequest.getLoginName(), PHONE);
            userDO = userMapper.selectOne(queryWrapper);
        } else if (RegexUtil.checkMail(loginRequest.getLoginName())) { // ? 为邮箱登录
            queryWrapper = query(loginRequest.getLoginName(), MAIL);
            userDO = userMapper.selectOne(queryWrapper);
        } else { // ? 都不是
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXIST);
        }

        // TODO 3. 数据库得到的用户信息进一步校验
        if (null == userDO) { // ? 为空情况
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_NULL);
        } else if (StringUtils.hasText(loginRequest.getMandatoryIdentity())
                && !loginRequest.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) { // ? 身份信息验证
            // ! 强制身份登录，身份校验不通过
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        } else if (!DigestUtil.sha256Hex(loginRequest.getPassword())
                .equals(userDO.getPassword())) { // ? 密码验证
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // TODO 4. 生成 token
        Map<String, Object> claim = Map.of("id", userDO.getId(),
                "identity", userDO.getIdentity());
        String token = JwtUtil.genJwt(claim);

        return new UserLoginDTO(token, UserIdentityEnum.forName(userDO.getIdentity()));
    }

    /**
     * 根据验证码登录
     * @param loginRequest 登录信息
     * @return UserLoginDTO
     */
    private UserLoginDTO loginByShortMessage(ShortMessageLoginRequest loginRequest) {
        // TODO 1. 校验手机号
        if (!RegexUtil.checkMobile(loginRequest.getLoginMobile())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        // TODO 2. 获取用户数据
        QueryWrapper<UserDO> queryWrapper = query(loginRequest.getLoginMobile(), PHONE);
        UserDO userDO = userMapper.selectOne(queryWrapper);

        // TODO 4. 校验用户信息
        if (null == userDO) { // ? 为空情况
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_NULL);
        } else if (StringUtils.hasText(loginRequest.getMandatoryIdentity())
                && !loginRequest.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) { // ? 身份信息验证
            // ! 强制身份登录，身份校验不通过
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        // TODO 5. 从 Redis 缓存中获取验证码
        String code = verificationCodeService.getVerificationCode(
                loginRequest.getLoginMobile());

        // TODO 6. 校验验证码
        if (null == code || !code.equals(loginRequest.getVerificationCode())) {
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);
        }

        // TODO 7. 生成 token
        Map<String, Object> claim = Map.of("id", userDO.getId(),
                "identity", userDO.getIdentity());
        String token = JwtUtil.genJwt(claim);

        return new UserLoginDTO(token, UserIdentityEnum.forName(userDO.getIdentity()));
    }

    /**
     * 检验注册参数
     * @param param 注册参数
     */
    private void checkRegisterInfo(UserRegisterRequest param) {
        // TODO 1. 校验注册是否为空
        if (null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }
        // TODO 2. 校验邮箱格式
        if (!RegexUtil.checkMail(param.getEmail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }
        // TODO 3. 校验手机号格式
        if (!RegexUtil.checkMobile(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
        // TODO 4. 校验注册身份信息
        if (null == UserIdentityEnum.forName(param.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        // TODO 5. 身份信息为管理员，校验是否填写密码
        if (param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                && !StringUtils.hasText(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
        }

        // TODO 6. 校验密码格式
        if (StringUtils.hasText(param.getPassword())
                && !RegexUtil.checkPassword(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // TODO 7. 校验邮箱是否被使用过
        if (checkMailUser(param.getEmail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }

        // TODO 8. 检验手机是否被使用过
        if (checkPhoneNumberUser(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }

    /**
     * 检验手机号
     * @param phoneNumber 手机号
     * @return 是否符合
     */
    private boolean checkPhoneNumberUser(String phoneNumber) {
        QueryWrapper<UserDO> queryWrapper = query(phoneNumber, PHONE);
        return userMapper.selectOne(queryWrapper) != null;
    }

    /**
     * 检验邮件
     * @param mail 邮件
     * @return 是否符合
     */
    private boolean checkMailUser(String mail) {
        QueryWrapper<UserDO> queryWrapper = query(mail, MAIL);
        return userMapper.selectOne(queryWrapper) != null;
    }

    /**
     * 封装 QueryMapper
     * @param param 需要比较的对象
     * @param option 根据邮箱还是手机号比较
     * @return 构造的 QueryMapper
     */
    private QueryWrapper<UserDO> query(String param, Integer option) {
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        if (option.equals(MAIL)) {
            queryWrapper.lambda()
                    .eq(UserDO::getEmail, param);
        } else if (option.equals(PHONE)){
            queryWrapper.lambda()
                    .eq(UserDO::getPhoneNumber, new Encrypt(param));
        } else {
            return null;
        }
        return queryWrapper;
    }
}
